/**
 * 
 */
package com.monkeyboy.security.authentication.mobile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.Assert;

/**
 * @Description
 *
 * @author Gavin<br>
 *         2019年8月27日
 */
public class SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
	// ~ Static fields/initializers
	// =====================================================================================

	public static final String MOBILE_KEY = "mobile";

	private String mobileParamter = MOBILE_KEY;// 携带手机号参数的名字
	private boolean postOnly = true;// 是不是只处理post请求

	// ~ Constructors
	// ===================================================================================================
	//匹配登录页提交的请求路径
	public SmsCodeAuthenticationFilter() {// 当前过滤器要处理的请求路径
		super(new AntPathRequestMatcher("/authentication/mobile", "POST"));
	}

	// ~ Methods
	// ========================================================================================================

	public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
			throws AuthenticationException {
		if (postOnly && !request.getMethod().equals("POST")) {
			throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
		}

		String mobile = obtainMobile(request);

		if (mobile == null) {
			mobile = "";
		}

		mobile = mobile.trim();
		//也是关键代码，实例化一个token用于存储mobile信息
		SmsCodeAuthenticationToken authRequest = new SmsCodeAuthenticationToken(mobile);// 注意这里构建的是一个未通过认证信息的authen,可查看源代码setAuthenticated（false）

		// Allow subclasses to set the "details" property
		setDetails(request, authRequest);// 把请求的信息设置到auth中
		//利用AuthenticationManager对象寻找provider开始认证
		return this.getAuthenticationManager().authenticate(authRequest);
	}

	// 获取手机号
	protected String obtainMobile(HttpServletRequest request) {
		return request.getParameter(mobileParamter);
	}

	/**
	 * Provided so that subclasses may configure what is put into the authentication
	 * request's details property.
	 *
	 * @param request     that an authentication request is being created for
	 * @param authRequest the authentication request object that should have its
	 *                    details set
	 */
	protected void setDetails(HttpServletRequest request, SmsCodeAuthenticationToken authRequest) {
		authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
	}

	/**
	 * Sets the parameter name which will be used to obtain the username from the
	 * login request.
	 *
	 * @param usernameParameter the parameter name. Defaults to "username".
	 */
	public void setMobileParamter(String mobileParamter) {
		Assert.hasText(mobileParamter, "mobileParamter parameter must not be empty or null");
		this.mobileParamter = mobileParamter;
	}

	/**
	 * Sets the parameter name which will be used to obtain the password from the
	 * login request..
	 *
	 * @param passwordParameter the parameter name. Defaults to "password".
	 */
	public void setPasswordParameter(String mobileParamter) {
		Assert.hasText(mobileParamter, "mobileParamter parameter must not be empty or null");
		this.mobileParamter = mobileParamter;
	}

	/**
	 * Defines whether only HTTP POST requests will be allowed by this filter. If
	 * set to true, and an authentication request is received which is not a POST
	 * request, an exception will be raised immediately and authentication will not
	 * be attempted. The <tt>unsuccessfulAuthentication()</tt> method will be called
	 * as if handling a failed authentication.
	 * <p>
	 * Defaults to <tt>true</tt> but may be overridden by subclasses.
	 */
	public void setPostOnly(boolean postOnly) {
		this.postOnly = postOnly;
	}

	public final String getMobileParamter() {
		return mobileParamter;
	}

	public final String setMobileParamter() {
		return mobileParamter;
	}
}
